home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Text⁄Files
/
Grep src
/
GrepPattern.c
< prev
Wrap
Text File
|
1986-10-30
|
10KB
|
532 lines
/*
GrepCompile - routines for compiling patterns into internal form,
and for matching strings against the compiled pattern.
*/
# include <DialogMgr.h>
# include "Grep.h"
/* special internal chars for compiled pattern */
# define CCL 1 /* match characters in class */
# define NCCL 2 /* all but characters in class */
# define CRANGE 3 /* range of chars */
# define ENDCCL 4 /* end char class */
# define ANY 5 /* match any char */
# define CLOSURE 6 /* closure */
# define EOL 7 /* end of line */
/* pattern compilation and matching vars */
static Boolean matchBol; /* match beginning of line? */
static int pix; /* index into pattern */
static int pMark;
static Boolean canClose;
/*
Pattern buffers. rawPattern is the text typed by user into
the dialog box. cmpPattern is the compiled pattern. Initially
they are both empty. (Format of the nil compiled pattern is
dependent on algorithm used to compile rawPattern.) This is
legal - it matches every line. If a file is grepped without
specifying a pattern, therefore, the whole file will be
displayed. A side effect of this is to turn grep on WORD files
into a WORD-to-TEXT file converter when the save output option
is turned on.
*/
char rawPattern[bufSiz] = "";
static char cmpPattern[bufSiz] = "";
/* ----------------------------------------------------------------------- */
/* pattern-compilation routines */
/* ----------------------------------------------------------------------- */
/*
Add char to pattern (may be a metachar, not necessarily
a literal character to match)
*/
AddPatChar (c)
char c;
{
if (ignoreCase && c >= 'A' && c <= 'Z')
c += 'a' - 'A';
cmpPattern[pix] = c;
++pix;
cmpPattern[pix] = 0;
} /* AddPatChar */
/*
Put a closure indicator into the pattern, in front of the
stuff that's to be closed.
*/
AddClosure ()
{
register int i;
++pix;
for (i = pix; i > pMark; --i)
/*loop (, i = pix, , --i <= pMark)*/
cmpPattern [i] = cmpPattern [i-1];
cmpPattern [pMark] = CLOSURE;
canClose = false;
} /* AddClosure */
/*
have found something that may be followed by a closure. set
canClose to indicate that fact, and set a mark to remember where
the closable thing is.
*/
MarkIt ()
{
pMark = pix; /* set mark in case closure comes up next */
canClose = true;
}
/*
Get escaped char (char following '\'). The only special one
right now is '\t', which is turned into a tab. Caller must check
that return value isn't zero.
*/
char EscapeChar (c)
char c;
{
return (c == 't' ? '\t' : c);
}
/*
compile character class. pass pointer to char after '[' that begins
the class pattern. Return nil if (error, else pointer to char
after closing ']' bracket.
*/
StringPtr Class (p)
register StringPtr p;
{
register char c, type, low, high;
type = CCL; /* 'character class' metachar */
if (*p == '^')
{
type = NCCL; /* 'match all but this class' metachar */
++p;
}
AddPatChar (type);
for (;;)
{
c = *p;
++p;
if (c == '\\')
{
c = EscapeChar (*p);
++p;
}
else if (c == ']')
break; /* end of class pattern */
if (c == 0)
return (nil); /* missing ']' - pattern error */
if (*p != '-')
AddPatChar (c);
else /* range */
{
low = c; /* low end */
++p;
high = *p; /* high end */
++p;
if (high == 0)
return (nil); /* pattern error */
AddPatChar (CRANGE);
AddPatChar (low);
AddPatChar (high);
}
}
AddPatChar (ENDCCL);
return (p); /* all ok */
} /* class */
/*
COMPILE - compile string into internal form suitable for efficient
pattern matching. String should be in C format.
*/
Boolean Compile (p)
register StringPtr p;
{
register char c;
pix = 0;
cmpPattern[0] = 0;
canClose = false;
matchBol = false;
/*
check for ^ - it's only special at beginning of line
*/
if (*p == '^')
{
matchBol = true;
++p;
}
for (;;)
{
c = *p;
++p;
if (c == '*')
{
/*
if (canClose is true, there was a preceding pattern which can be
closed (not closure, ^ or $), so close it. otherwise, take *
literally.
*/
if (canClose) /* something to close */
{
AddClosure ();
continue;
}
}
/*
$ only special at end of line
*/
if (c == '$' && *p == 0)
{
AddPatChar ((char) EOL);
continue;
}
/*
at this point we know we have a character that can be followed by a
closure, so mark the pattern position.
*/
MarkIt ();
/*
use most escaped chars literally, except null, which is an error,
and \t, which is a tab.
*/
if (c == '\\')
{
c = EscapeChar (*p++);
if (c == 0)
return (false); /* pattern error */
AddPatChar (c);
continue;
}
if (c == 0) break; /* done compiling */
switch (c)
{
case '.': AddPatChar (ANY); break; /* match any char */
case '[': /* match character class */
{
if ((p = Class (p)) == nil)
return (false); /* class pattern error */
break;
}
default: AddPatChar (c); /* match char literally */
}
} /* loop */
return (true); /* all ok */
} /* compile */
/* ----------------------------------------------------------------------- */
/* pattern-matching routines */
/* ----------------------------------------------------------------------- */
/*
NEXTPOS - find position in pattern of next component to match
*/
StringPtr NextPos (p)
register StringPtr p;
{
register char c;
c = *p;
++p;
if (c == CCL || c == NCCL)
{
do /* look for end of class stuff */
{
c = *p;
++p;
} while (c != ENDCCL);
}
return (p);
} /* NextPos */
Boolean InClass (c, p)
register char c;
register StringPtr p;
{
register char high, low, pc;
for (;;)
{
pc = *p;
++p;
if (pc == ENDCCL)
return (false);
if (pc == CRANGE) /* range */
{
low = *p;
++p;
high = *p;
++p;
if (low <= c && c <= high)
break; /* it's within the range */
}
else if (c == pc)
break; /* it matched this char of class */
}
return (true);
} /* InClass */
/*
OMATCH - match character c against the current pattern position.
*/
Boolean omatch (c, p)
register char c;
register StringPtr p;
{
register char pc;
if (ignoreCase && c >= 'A' && c <= 'Z')
c += 'a' - 'A';
pc = *p;
++p;
switch (pc)
{
case CCL: return (InClass (c, p));
case NCCL: return (!InClass (c, p));
case ANY: return (c != 0); /* don't match end of line */
default: return (c == pc);
}
} /* omatch */
/*
try to match pattern p at the given position in string s
*/
Boolean amatch (s, p)
register StringPtr s, p;
{
register StringPtr cursp;
if (*p == 0)
return (true); /* end of pattern, have matched it */
if (*p == EOL)
return (*s == 0); /* must be end of string to match EOL */
if (*p == CLOSURE)
{
/*
advance as far as possible, matching the current pattern position.
when omatch fails, s will point 1 past the character that failed.
then back up one and try to match rest of pattern. if that fails,
keep retreating until back at point of original closure start
*/
++p; /* skip closure marker */
cursp = s; /* save current string position */
while (omatch (*s++, p))
/* march! */ ;
do /* keep backing up */
{
--s;
if (amatch (s, NextPos (p)))
return (true);
} while (s > cursp);
return (false);
}
if (omatch (*s++, p))
return (amatch (s, NextPos (p)));
return (false);
} /* amatch */
/*
MATCH - match string s against the compiled pattern
if matchBol is true, then anchor the match to the beginning of the
string, else try the pattern against successive string positions until
the match succeeds or the end of the string is reached.
s should be in C format.
*/
Boolean match (s)
register StringPtr s;
{
if (matchBol) /* anchored match */
{
return (amatch (s, cmpPattern));
}
for (;;) /* floating match */
{
if (amatch (s, cmpPattern))
return (true);
if (*s == 0)
return (false); /* end of string but no match */
++s;
}
} /* match */
/*
Routines for presenting the pattern entry dialog.
*/
/*
pattern dialog items
item type
1 ok button
2 cancel button
3 prompt
4 edittext item for typing in pattern
5 "lines not containing pattern" radio button
6 "print line numbers" check box
7 "ignore case" check box
Puts the string entered into theString, which on return is
empty if either the user clicked Cancel or typed no string
and clicked ok.
*/
enum
{
okButton = 1,
cancelButton,
promptStatText,
patText,
lineOption,
numberOption,
caseOption
};
static DialogPtr theDialog;
/*
Set or get the value of a checkbox (i.e., boolean) dialog item
*/
SetDBoolean (itemNo, itemValue)
int itemNo;
Boolean itemValue;
{
Handle itemHandle;
int itemType;
Rect r;
GetDItem (theDialog, itemNo, &itemType, &itemHandle, &r);
/*
Note type conversion here. True turns the control on.
*/
SetCtlValue (itemHandle, (int) itemValue);
}
Boolean GetDBoolean (itemNo)
int itemNo;
{
Handle itemHandle;
int itemType;
Rect r;
GetDItem (theDialog, itemNo, &itemType, &itemHandle, &r);
return ((Boolean) GetCtlValue (itemHandle));
}
Boolean GetPatDlog ()
{
int itemNo, itemType;
Handle itemHandle;
Rect r;
register Boolean result;
theDialog = GetNewDialog (resBase + patBox, nil, -1L);
SetDBoolean (lineOption, !prtMatches);
SetDBoolean (numberOption, prtLineNum);
SetDBoolean (caseOption, ignoreCase);
GetDItem (theDialog, patText, &itemType, &itemHandle, &r);
SetIText (itemHandle, rawPattern);
SelIText (theDialog, patText, 0, 32760);
ShowWindow (theDialog);
for (;;)
{
ModalDialog (nil, &itemNo);
if (itemNo == cancelButton)
{
result = false;
break;
}
else if (itemNo == okButton)
{
GetDItem (theDialog, patText, &itemType, &itemHandle, &r);
GetIText (itemHandle, rawPattern);
prtMatches = !GetDBoolean (lineOption);
prtLineNum = GetDBoolean (numberOption);
ignoreCase = GetDBoolean (caseOption);
result = true;
break;
}
else /* must be option check box - flip value */
{
SetDBoolean (itemNo, !GetDBoolean (itemNo));
}
}
DisposDialog (theDialog);
return (result);
} /* GetPatDlog */
GetGrepPat ()
{
if (GetPatDlog ())
{
PtoCstr (rawPattern);
havePat = Compile (rawPattern);
CtoPstr (rawPattern);
if (!havePat)
Alarm ("\pBad Pattern");
}
}